home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Very Best of Atari Inside
/
The Very Best of Atari Inside 1.iso
/
mint
/
mint99s
/
dosdir.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-01-16
|
30KB
|
1,305 lines
/*
Copyright 1990,1991,1992 Eric R. Smith.
Copyright 1992,1993 Atari Corporation.
All rights reserved.
*/
/* DOS directory functions */
#include "mint.h"
/* change to a new drive: should always return a map of valid drives */
long ARGS_ON_STACK
d_setdrv(d)
int d;
{
long r;
extern long dosdrvs; /* in filesys.c */
r = drvmap() | dosdrvs | PSEUDODRVS;
TRACE(("Dsetdrv(%d)", d));
if (d < 0 || d >= NUM_DRIVES || (r & (1L << d)) == 0) {
DEBUG(("Dsetdrv: invalid drive %d", d));
return r;
}
curproc->base->p_defdrv = curproc->curdrv = d;
return r;
}
long ARGS_ON_STACK
d_getdrv()
{
TRACE(("Dgetdrv"));
return curproc->curdrv;
}
long ARGS_ON_STACK
d_free(buf, d)
long *buf;
int d;
{
fcookie *dir = 0;
extern int aliasdrv[];
TRACE(("Dfree(%d)", d));
/* drive 0 means current drive, otherwise it's d-1 */
if (d)
d = d-1;
else
d = curproc->curdrv;
/* Hack to make programs (like df) which use drive
* information from Fxattr() work more often.
* BUG: this works only if the drive is a root,
* a current directory or an alias, or one of the
* standard drives.
*/
if (d < 0 || d >= NUM_DRIVES) {
int i;
for (i = 0; i < NUM_DRIVES; i++) {
if (aliasdrv[i] == d) {
d = i;
goto aliased;
}
if (curproc->curdir[i].dev == d) {
dir = &curproc->curdir[i];
} else if (curproc->root[i].dev == d) {
dir = &curproc->root[i];
}
}
if (dir && dir->fs) {
return (*dir->fs->dfree)(dir, buf);
}
return EDRIVE;
}
/* check for a media change -- we don't care much either way, but it
* does keep the results more accurate
*/
(void)disk_changed(d);
aliased:
/* use current directory, not root, since it's more likely that
* programs are interested in the latter (this makes U: work much
* better)
*/
dir = &curproc->curdir[d];
if (!dir->fs) {
DEBUG(("Dfree: bad drive"));
return EDRIVE;
}
return (*dir->fs->dfree)(dir, buf);
}
/* temp1 is a convenient place for path2fs puts the last component of
* the path name
*/
extern char temp1[PATH_MAX]; /* in filesys.c */
long ARGS_ON_STACK
d_create(path)
const char *path;
{
fcookie dir;
long r;
TRACE(("Dcreate(%s)", path));
r = path2cookie(path, temp1, &dir);
if (r) {
DEBUG(("Dcreate(%s): returning %ld", path, r));
return r; /* an error occured */
}
/* check for write permission on the directory */
r = dir_access(&dir, S_IWOTH);
if (r) {
DEBUG(("Dcreate(%s): access to directory denied",path));
release_cookie(&dir);
return r;
}
r = (*dir.fs->mkdir)(&dir, temp1, DEFAULT_DIRMODE & ~curproc->umask);
release_cookie(&dir);
return r;
}
long ARGS_ON_STACK
d_delete(path)
const char *path;
{
fcookie parentdir, targdir;
long r;
PROC *p;
int i;
XATTR xattr;
TRACE(("Ddelete(%s)", path));
r = path2cookie(path, temp1, &parentdir);
if (r) {
DEBUG(("Ddelete(%s): error %lx", path, r));
release_cookie(&parentdir);
return r;
}
/* check for write permission on the directory which the target
* is located
*/
if ((r = dir_access(&parentdir, S_IWOTH)) != 0) {
DEBUG(("Ddelete(%s): access to directory denied", path));
release_cookie(&parentdir);
return r;
}
/* now get the info on the file itself */
r = relpath2cookie(&parentdir, temp1, NULL, &targdir, 0);
if (r) {
bailout:
release_cookie(&parentdir);
DEBUG(("Ddelete: error %ld on %s", r, path));
return r;
}
if ((r = (*targdir.fs->getxattr)(&targdir, &xattr)) != 0) {
release_cookie(&targdir);
goto bailout;
}
/* if the "directory" is a symbolic link, really unlink it */
if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
r = (*parentdir.fs->remove)(&parentdir, temp1);
} else if ( (xattr.mode & S_IFMT) != S_IFDIR ) {
DEBUG(("Ddelete: %s is not a directory", path));
r = EPTHNF;
} else {
/* don't delete anyone else's root or current directory */
for (p = proclist; p; p = p->gl_next) {
if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
continue;
for (i = 0; i < NUM_DRIVES; i++) {
if (samefile(&targdir, &p->root[i])) {
DEBUG(("Ddelete: directory %s is a root directory",
path));
noaccess:
release_cookie(&targdir);
release_cookie(&parentdir);
return EACCDN;
} else if (samefile(&targdir, &p->curdir[i])) {
if (i == p->curdrv && p != curproc) {
DEBUG(("Ddelete: directory %s is in use",
path));
goto noaccess;
} else {
release_cookie(&p->curdir[i]);
dup_cookie(&p->curdir[i], &p->root[i]);
}
}
}
}
release_cookie(&targdir);
r = (*parentdir.fs->rmdir)(&parentdir, temp1);
}
release_cookie(&parentdir);
return r;
}
long ARGS_ON_STACK
d_setpath(path)
const char *path;
{
fcookie dir;
int drv = curproc->curdrv;
int i;
char c;
long r;
XATTR xattr;
TRACE(("Dsetpath(%s)", path));
r = path2cookie(path, follow_links, &dir);
if (r) {
DEBUG(("Dsetpath(%s): returning %ld", path, r));
return r;
}
if (path[0] && path[1] == ':') {
c = *path;
if (c >= 'a' && c <= 'z')
drv = c-'a';
else if (c >= 'A' && c <= 'Z')
drv = c-'A';
}
r = (*dir.fs->getxattr)(&dir, &xattr);
if (r < 0) {
DEBUG(("Dsetpath: file '%s': attributes not found", path));
release_cookie(&dir);
return r;
}
if (!(xattr.attr & FA_DIR)) {
DEBUG(("Dsetpath(%s): not a directory",path));
release_cookie(&dir);
return EPTHNF;
}
/*
* watch out for symbolic links; if c:\foo is a link to d:\bar, then
* "cd c:\foo" should also change the drive to d:
*/
if (drv != UNIDRV && dir.dev != curproc->root[drv].dev) {
for (i = 0; i < NUM_DRIVES; i++) {
if (curproc->root[i].dev == dir.dev &&
curproc->root[i].fs == dir.fs) {
if (drv == curproc->curdrv)
curproc->curdrv = i;
drv = i;
break;
}
}
}
release_cookie(&curproc->curdir[drv]);
curproc->curdir[drv] = dir;
return 0;
}
/* jr: like d_getpath, except that the caller provides a limit
for the max. number of characters to be put into the buffer.
Inspired by POSIX.1, getcwd(), 5.2.2 */
long ARGS_ON_STACK
d_getcwd(path, drv, size)
char *path;
int drv, size;
{
fcookie *dir, *root;
long r;
char buf[PATH_MAX];
FILESYS *fs;
TRACE(("Dgetcwd(%c, %d)", drv + '@', size));
if (drv < 0 || drv > NUM_DRIVES)
return EDRIVE;
drv = (drv == 0) ? curproc->curdrv : drv-1;
root = &curproc->root[drv];
if (!root->fs) { /* maybe not initialized yet? */
changedrv(drv);
root = &curproc->curdir[drv];
if (!root->fs)
return EDRIVE;
}
fs = root->fs;
dir = &curproc->curdir[drv];
if (!(fs->fsflags & FS_LONGPATH)) {
r = (*fs->getname)(root, dir, buf, PATH_MAX);
if (r) return r;
if (strlen(buf) < size) {
strcpy(path, buf);
return 0;
} else {
return PATH_MAX;
}
}
return (*fs->getname)(root, dir, path, size);
}
long ARGS_ON_STACK
d_getpath(path, drv)
char *path;
int drv;
{
TRACE(("Dgetpath(%c)", drv + '@'));
return d_getcwd(path, drv, PATH_MAX);
}
long ARGS_ON_STACK
f_setdta(dta)
DTABUF *dta;
{
TRACE(("Fsetdta: %lx", dta));
curproc->dta = dta;
curproc->base->p_dta = (char *)dta;
return 0;
}
long ARGS_ON_STACK
f_getdta()
{
long r;
r = (long)curproc->dta;
TRACE(("Fgetdta: returning %lx", r));
return r;
}
/*
* Fsfirst/next are actually implemented in terms of opendir/readdir/closedir.
*/
long ARGS_ON_STACK
f_sfirst(path, attrib)
const char *path;
int attrib;
{
char *s, *slash;
FILESYS *fs;
fcookie dir, newdir;
DTABUF *dta;
DIR *dirh;
XATTR xattr;
long r;
int i, havelabel;
TRACE(("Fsfirst(%s, %x)", path, attrib));
r = path2cookie(path, temp1, &dir);
if (r) {
DEBUG(("Fsfirst(%s): path2cookie returned %ld", path, r));
return r;
}
/*
* we need to split the last name (which may be a pattern) off from
* the rest of the path, even if FS_KNOPARSE is true
*/
slash = 0;
s = temp1;
while (*s) {
if (*s == '\\')
slash = s;
s++;
}
if (slash) {
*slash++ = 0; /* slash now points to a name or pattern */
r = relpath2cookie(&dir, temp1, follow_links, &newdir, 0);
release_cookie(&dir);
if (r) {
DEBUG(("Fsfirst(%s): lookup returned %ld", path, r));
return r;
}
dir = newdir;
} else {
slash = temp1;
}
/* BUG? what if there really is an empty file name? */
if (!*slash) {
DEBUG(("Fsfirst: empty pattern"));
return EFILNF;
}
fs = dir.fs;
dta = curproc->dta;
/* Now, see if we can find a DIR slot for the search. We use the following
* heuristics to try to avoid destroying a slot:
* (1) if the search doesn't use wildcards, don't bother with a slot
* (2) if an existing slot was for the same DTA address, re-use it
* (3) if there's a free slot, re-use it. Slots are freed when the
* corresponding search is terminated.
*/
for (i = 0; i < NUM_SEARCH; i++) {
if (curproc->srchdta[i] == dta) {
dirh = &curproc->srchdir[i];
if (dirh->fc.fs) {
(*dirh->fc.fs->closedir)(dirh);
release_cookie(&dirh->fc);
dirh->fc.fs = 0;
}
curproc->srchdta[i] = 0; /* slot is now free */
}
}
/* copy the pattern over into dta_pat into TOS 8.3 form */
/* remember that "slash" now points at the pattern (it follows the last \,
if any)
*/
copy8_3(dta->dta_pat, slash);
/* if attrib & FA_LABEL, read the volume label */
/* BUG: the label date and time are wrong. Does it matter?
*/
havelabel = 0;
if (attrib & FA_LABEL) {
r = (*fs->readlabel)(&dir, dta->dta_name, TOS_NAMELEN+1);
dta->dta_attrib = FA_LABEL;
dta->dta_time = dta->dta_date = 0;
dta->dta_size = 0;
dta->magic = EVALID;
if (r == 0 && !pat_match(dta->dta_name, dta->dta_pat))
r = EFILNF;
if (attrib == FA_LABEL)
return r;
else if (r == 0)
havelabel = 1;
}
if (!havelabel && has_wild(slash) == 0) { /* no wild cards in pattern */
r = relpath2cookie(&dir, slash, follow_links, &newdir, 0);
if (r == 0) {
r = (*newdir.fs->getxattr)(&newdir, &xattr);
release_cookie(&newdir);
}
release_cookie(&dir);
if (r) {
DEBUG(("Fsfirst(%s): couldn't get file attributes",path));
return r;
}
dta->magic = EVALID;
dta->dta_attrib = xattr.attr;
dta->dta_time = xattr.mtime;
dta->dta_date = xattr.mdate;
dta->dta_size = xattr.size;
strncpy(dta->dta_name, slash, TOS_NAMELEN-1);
dta->dta_name[TOS_NAMELEN-1] = 0;
if (curproc->domain == DOM_TOS &&
!(fs->fsflags & FS_CASESENSITIVE))
strupr(dta->dta_name);
return 0;
}
/* There is a wild card. Try to find a slot for an opendir/readdir
* search. NOTE: we also come here if we were asked to search for
* volume labels and found one.
*/
for (i = 0; i < NUM_SEARCH; i++) {
if (curproc->srchdta[i] == 0)
break;
}
if (i == NUM_SEARCH) {
int oldest = 0; long oldtime = curproc->srchtim[0];
DEBUG(("Fsfirst(%s): having to re-use a directory slot!",path));
for (i = 1; i < NUM_SEARCH; i++) {
if (curproc->srchtim[i] < oldtime) {
oldest = i;
oldtime = curproc->srchtim[i];
}
}
/* OK, close this directory for re-use */
i = oldest;
dirh = &curproc->srchdir[i];
if (dirh->fc.fs) {
(*dirh->fc.fs->closedir)(dirh);
release_cookie(&dirh->fc);
dirh->fc.fs = 0;
}
curproc->srchdta[i] = 0;
}
/* check to see if we have read permission on the directory (and make
* sure that it really is a directory!)
*/
r = dir_access(&dir, S_IROTH);
if (r) {
DEBUG(("Fsfirst(%s): access to directory denied (error code %ld)", path, r));
release_cookie(&dir);
return r;
}
/* set up the directory for a search */
dirh = &curproc->srchdir[i];
dirh->fc = dir;
dirh->index = 0;
dirh->flags = TOS_SEARCH;
r = (*dir.fs->opendir)(dirh, dirh->flags);
if (r != 0) {
DEBUG(("Fsfirst(%s): couldn't open directory (error %ld)",
path, r));
release_cookie(&dir);
return r;
}
/* mark the slot as in-use */
curproc->srchdta[i] = dta;
/* set up the DTA for Fsnext */
dta->index = i;
dta->magic = SVALID;
dta->dta_sattrib = attrib;
/* OK, now basically just do Fsnext, except that instead of ENMFIL we
* return EFILNF.
* NOTE: If we already have found a volume label from the search above,
* then we skip the f_snext and just return that.
*/
if (havelabel)
return 0;
r = f_snext();
if (r == ENMFIL) r = EFILNF;
if (r)
TRACE(("Fsfirst: returning %ld", r));
/* release_cookie isn't necessary, since &dir is now stored in the
* DIRH structure and will be released when the search is completed
*/
return r;
}
/*
* Counter for Fsfirst/Fsnext, so that we know which search slots are
* least recently used. This is updated once per second by the code
* in timeout.c.
* BUG: 1/second is pretty low granularity
*/
long searchtime;
long ARGS_ON_STACK
f_snext()
{
static char buf[TOS_NAMELEN+1];
DTABUF *dta = curproc->dta;
FILESYS *fs;
fcookie fc;
int i;
DIR *dirh;
long r;
XATTR xattr;
TRACE(("Fsnext"));
if (dta->magic == EVALID) {
DEBUG(("Fsnext: DTA marked a failing search"));
return ENMFIL;
}
if (dta->magic != SVALID) {
DEBUG(("Fsnext: dta incorrectly set up"));
return EINVFN;
}
i = dta->index;
dirh = &curproc->srchdir[i];
curproc->srchtim[i] = searchtime;
fs = dirh->fc.fs;
if (!fs) /* oops -- the directory got closed somehow */
return EINTRN;
/* BUG: f_snext and readdir should check for disk media changes */
for(;;) {
r = (*fs->readdir)(dirh, buf, TOS_NAMELEN+1, &fc);
if (r == ENAMETOOLONG) {
DEBUG(("Fsnext: name too long"));
continue; /* TOS programs never see these names */
}
if (r != 0) {
baderror:
if (dirh->fc.fs)
(void)(*fs->closedir)(dirh);
release_cookie(&dirh->fc);
dirh->fc.fs = 0;
curproc->srchdta[i] = 0;
dta->magic = EVALID;
if (r != ENMFIL)
DEBUG(("Fsnext: returning %ld", r));
return r;
}
if (!pat_match(buf, dta->dta_pat))
continue; /* different patterns */
/* check for search attributes */
r = (*fc.fs->getxattr)(&fc, &xattr);
release_cookie(&fc);
if (r) {
DEBUG(("Fsnext: couldn't get file attributes"));
goto baderror;
}
/* if the file is a symbolic link, try to find what it's linked to */
if ( (xattr.mode & S_IFMT) == S_IFLNK ) {
char linkedto[PATH_MAX];
r = (*fc.fs->readlink)(&fc, linkedto, PATH_MAX);
if (r == 0) {
/* the "1" tells relpath2cookie that we read a link */
r = relpath2cookie(&dirh->fc, linkedto,
follow_links, &fc, 1);
if (r == 0)
r = (*fc.fs->getxattr)(&fc, &xattr);
}
if (r) {
DEBUG(("Fsnext: couldn't follow link: error %ld",
r));
}
}
/* silly TOS rules for matching attributes */
if (xattr.attr == 0) break;
if (xattr.attr & 0x21) break;
if (dta->dta_sattrib & xattr.attr)
break;
}
/* here, we have a match */
dta->dta_attrib = xattr.attr;
dta->dta_time = xattr.mtime;
dta->dta_date = xattr.mdate;
dta->dta_size = xattr.size;
strcpy(dta->dta_name, buf);
if (curproc->domain == DOM_TOS && !(fs->fsflags & FS_CASESENSITIVE)) {
strupr(dta->dta_name);
}
return 0;
}
long ARGS_ON_STACK
f_attrib(name, rwflag, attr)
const char *name;
int rwflag;
int attr;
{
fcookie fc;
XATTR xattr;
long r;
TRACE(("Fattrib(%s, %d)", name, attr));
r = path2cookie(name, (char *)0, &fc);
if (r) {
DEBUG(("Fattrib(%s): error %ld", name, r));
return r;
}
r = (*fc.fs->getxattr)(&fc, &xattr);
release_cookie(&fc);
if (r) {
DEBUG(("Fattrib(%s): getxattr returned %ld", name, r));
return r;
}
if (rwflag) {
if (attr & (FA_LABEL|FA_DIR)) {
DEBUG(("Fattrib(%s): illegal attributes specified",name));
return EACCDN;
} else if (curproc->euid && curproc->euid != xattr.uid) {
DEBUG(("Fattrib(%s): not the file's owner",name));
return EACCDN;
} else if (xattr.attr & (FA_LABEL|FA_DIR)) {
DEBUG(("Fattrib(%s): file is a volume label "
"or directory",name));
return EACCDN;
}
return (*fc.fs->chattr)(&fc, attr);
} else {
return xattr.attr;
}
}
long ARGS_ON_STACK
f_delete(name)
const char *name;
{
fcookie dir;
long r;
TRACE(("Fdelete(%s)", name));
r = path2cookie(name, temp1, &dir);
if (r) {
DEBUG(("Fdelete: error %ld", r));
return r;
}
/* check for write permission on directory */
r = dir_access(&dir, S_IWOTH);
if (r) {
DEBUG(("Fdelete(%s): write access to directory denied",name));
} else {
/* BUG: we should check here for a read-only file */
r = (*dir.fs->remove)(&dir,temp1);
}
release_cookie(&dir);
return r;
}
long ARGS_ON_STACK
f_rename(junk, old, new)
int junk; /* ignored, for TOS compatibility */
const char *old, *new;
{
fcookie olddir, newdir, oldfil;
XATTR xattr;
char temp2[PATH_MAX];
long r;
UNUSED(junk);
TRACE(("Frename(%s, %s)", old, new));
r = path2cookie(old, temp2, &olddir);
if (r) {
DEBUG(("Frename(%s,%s): error parsing old name",old,new));
return r;
}
/* check for permissions on the old file
* GEMDOS doesn't allow rename if the file is FA_RDONLY
* we enforce this restriction only on regular files; processes,
* directories, and character special files can be renamed at will
*/
r = relpath2cookie(&olddir, temp2, follow_links, &oldfil, 0);
if (r) {
DEBUG(("Frename(%s,%s): old file not found",old,new));
release_cookie(&olddir);
return r;
}
r = (*oldfil.fs->getxattr)(&oldfil, &xattr);
if (r ||
((xattr.mode & S_IFMT) == S_IFREG && (xattr.attr & FA_RDONLY)) )
{
DEBUG(("Frename(%s,%s): access to old file not granted",old,new));
release_cookie(&olddir);
release_cookie(&oldfil);
return EACCDN;
}
r = path2cookie(new, temp1, &newdir);
if (r) {
DEBUG(("Frename(%s,%s): error parsing new name",old,new));
release_cookie(&olddir);
release_cookie(&oldfil);
return r;
}
if (newdir.fs != olddir.fs) {
DEBUG(("Frename(%s,%s): different file systems",old,new));
release_cookie(&olddir);
release_cookie(&oldfil);
release_cookie(&newdir);
return EXDEV; /* cross device rename */
}
/* check for write permission on both directories */
r = dir_access(&olddir, S_IWOTH);
if (!r) r = dir_access(&newdir, S_IWOTH);
if (r) {
DEBUG(("Frename(%s,%s): access to a directory denied",old,new));
} else {
r = (*newdir.fs->rename)(&olddir, temp2, &newdir, temp1);
}
release_cookie(&olddir);
release_cookie(&oldfil);
release_cookie(&newdir);
return r;
}
/*
* GEMDOS extension: Dpathconf(name, which)
* returns information about filesystem-imposed limits; "name" is the name
* of a file or directory about which the limit information is requested;
* "which" is the limit requested, as follows:
* -1 max. value of "which" allowed
* 0 internal limit on open files, if any
* 1 max. number of links to a file {LINK_MAX}
* 2 max. path name length {PATH_MAX}
* 3 max. file name length {NAME_MAX}
* 4 no. of bytes in atomic write to FIFO {PIPE_BUF}
* 5 file name truncation rules
* 6 file name case translation rules
*
* unlimited values are returned as 0x7fffffffL
*
* see also Sysconf() in dos.c
*/
long ARGS_ON_STACK
d_pathconf(name, which)
const char *name;
int which;
{
fcookie dir;
long r;
r = path2cookie(name, (char *)0, &dir);
if (r) {
DEBUG(("Dpathconf(%s): bad path",name));
return r;
}
r = (*dir.fs->pathconf)(&dir, which);
if (which == DP_CASE && r == EINVFN) {
/* backward compatibility with old .XFS files */
r = (dir.fs->fsflags & FS_CASESENSITIVE) ? DP_CASESENS :
DP_CASEINSENS;
}
release_cookie(&dir);
return r;
}
/*
* GEMDOS extension: Opendir/Readdir/Rewinddir/Closedir offer a new,
* POSIX-like alternative to Fsfirst/Fsnext, and as a bonus allow for
* arbitrary length file names
*/
long ARGS_ON_STACK
d_opendir(name, flag)
const char *name;
int flag;
{
DIR *dirh;
fcookie dir;
long r;
r = path2cookie(name, follow_links, &dir);
if (r) {
DEBUG(("Dopendir(%s): error %ld", name, r));
return r;
}
r = dir_access(&dir, S_IROTH);
if (r) {
DEBUG(("Dopendir(%s): read permission denied", name));
release_cookie(&dir);
return r;
}
dirh = (DIR *)kmalloc(SIZEOF(DIR));
if (!dirh) {
release_cookie(&dir);
return ENSMEM;
}
dirh->fc = dir;
dirh->index = 0;
dirh->flags = flag;
r = (*dir.fs->opendir)(dirh, flag);
if (r) {
DEBUG(("d_opendir(%s): opendir returned %ld", name, r));
release_cookie(&dir);
kfree(dirh);
return r;
}
/* we keep a chain of open directories so that if a process
* terminates without closing them all, we can clean up
*/
dirh->next = curproc->searches;
curproc->searches = dirh;
return (long)dirh;
}
long ARGS_ON_STACK
d_readdir(len, handle, buf)
int len;
long handle;
char *buf;
{
DIR *dirh = (DIR *)handle;
fcookie fc;
long r;
if (!dirh->fc.fs)
return EIHNDL;
r = (*dirh->fc.fs->readdir)(dirh, buf, len, &fc);
if (r == 0)
release_cookie(&fc);
return r;
}
long ARGS_ON_STACK
d_rewind(handle)
long handle;
{
DIR *dirh = (DIR *)handle;
if (!dirh->fc.fs)
return EIHNDL;
return (*dirh->fc.fs->rewinddir)(dirh);
}
/*
* NOTE: there is also code in terminate() in dosmem.c that
* does automatic closes of directory searches.
* If you change d_closedir(), you may also need to change
* terminate().
*/
long ARGS_ON_STACK
d_closedir(handle)
long handle;
{
long r;
DIR *dirh = (DIR *)handle;
DIR **where;
if (!dirh->fc.fs)
return EIHNDL;
where = &curproc->searches;
while (*where && *where != dirh) {
where = &((*where)->next);
}
if (!*where) {
DEBUG(("Dclosedir: not an open directory"));
return EIHNDL;
}
/* unlink the directory from the chain */
*where = dirh->next;
r = (*dirh->fc.fs->closedir)(dirh);
release_cookie(&dirh->fc);
dirh->fc.fs = 0;
if (r) {
DEBUG(("Dclosedir: error %ld", r));
}
kfree(dirh);
return r;
}
/*
* GEMDOS extension: Fxattr gets extended attributes for a file. "flag"
* is 0 if symbolic links are to be followed (like stat), 1 if not (like
* lstat).
*/
long ARGS_ON_STACK
f_xattr(flag, name, xattr)
int flag;
const char *name;
XATTR *xattr;
{
fcookie fc;
long r;
TRACE(("Fxattr(%d, %s)", flag, name));
r = path2cookie(name, flag ? (char *)0 : follow_links, &fc);
if (r) {
DEBUG(("Fxattr(%s): path2cookie returned %ld", name, r));
return r;
}
r = (*fc.fs->getxattr)(&fc, xattr);
if (r) {
DEBUG(("Fxattr(%s): returning %ld", name, r));
}
release_cookie(&fc);
return r;
}
/*
* GEMDOS extension: Flink(old, new) creates a hard link named "new"
* to the file "old".
*/
long ARGS_ON_STACK
f_link(old, new)
const char *old, *new;
{
fcookie olddir, newdir;
char temp2[PATH_MAX];
long r;
TRACE(("Flink(%s, %s)", old, new));
r = path2cookie(old, temp2, &olddir);
if (r) {
DEBUG(("Flink(%s,%s): error parsing old name",old,new));
return r;
}
r = path2cookie(new, temp1, &newdir);
if (r) {
DEBUG(("Flink(%s,%s): error parsing new name",old,new));
release_cookie(&olddir);
return r;
}
if (newdir.fs != olddir.fs) {
DEBUG(("Flink(%s,%s): different file systems",old,new));
release_cookie(&olddir);
release_cookie(&newdir);
return EXDEV; /* cross device link */
}
/* check for write permission on the destination directory */
r = dir_access(&newdir, S_IWOTH);
if (r) {
DEBUG(("Flink(%s,%s): access to directory denied",old,new));
} else
r = (*newdir.fs->hardlink)(&olddir, temp2, &newdir, temp1);
release_cookie(&olddir);
release_cookie(&newdir);
return r;
}
/*
* GEMDOS extension: Fsymlink(old, new): create a symbolic link named
* "new" that contains the path "old".
*/
long ARGS_ON_STACK
f_symlink(old, new)
const char *old, *new;
{
fcookie newdir;
long r;
TRACE(("Fsymlink(%s, %s)", old, new));
r = path2cookie(new, temp1, &newdir);
if (r) {
DEBUG(("Fsymlink(%s,%s): error parsing %s", old,new,new));
return r;
}
r = dir_access(&newdir, S_IWOTH);
if (r) {
DEBUG(("Fsymlink(%s,%s): access to directory denied",old,new));
} else
r = (*newdir.fs->symlink)(&newdir, temp1, old);
release_cookie(&newdir);
return r;
}
/*
* GEMDOS extension: Freadlink(buflen, buf, linkfile):
* read the contents of the symbolic link "linkfile" into the buffer
* "buf", which has length "buflen".
*/
long ARGS_ON_STACK
f_readlink(buflen, buf, linkfile)
int buflen;
char *buf;
const char *linkfile;
{
fcookie file;
long r;
XATTR xattr;
TRACE(("Freadlink(%s)", linkfile));
r = path2cookie(linkfile, (char *)0, &file);
if (r) {
DEBUG(("Freadlink: unable to find %s", linkfile));
return r;
}
r = (*file.fs->getxattr)(&file, &xattr);
if (r) {
DEBUG(("Freadlink: unable to get attributes for %s", linkfile));
} else if ( (xattr.mode & S_IFMT) == S_IFLNK )
r = (*file.fs->readlink)(&file, buf, buflen);
else {
DEBUG(("Freadlink: %s is not a link", linkfile));
r = EACCDN;
}
release_cookie(&file);
return r;
}
/*
* GEMDOS extension: Dcntl(): do file system specific functions
*/
long ARGS_ON_STACK
d_cntl(cmd, name, arg)
int cmd;
const char *name;
long arg;
{
fcookie dir;
long r;
TRACE(("Dcntl(cmd=%x, file=%s, arg=%lx)", cmd, name, arg));
r = path2cookie(name, temp1, &dir);
if (r) {
DEBUG(("Dcntl: couldn't find %s", name));
return r;
}
r = (*dir.fs->fscntl)(&dir, temp1, cmd, arg);
release_cookie(&dir);
return r;
}
/*
* GEMDOS extension: Fchown(name, uid, gid) changes the user and group
* ownerships of a file to "uid" and "gid" respectively.
*/
long ARGS_ON_STACK
f_chown(name, uid, gid)
const char *name;
int uid, gid;
{
fcookie fc;
XATTR xattr;
long r;
TRACE(("Fchown(%s, %d, %d)", name, uid, gid));
r = path2cookie(name, follow_links, &fc);
if (r) {
DEBUG(("Fchown(%s): error %ld", name, r));
return r;
}
/* MiNT acts like _POSIX_CHOWN_RESTRICTED: a non-privileged process can
* only change the ownership of a file that is owned by this user, to
* the effective group id of the process
*/
if (curproc->euid) {
if (curproc->egid != gid)
r = EACCDN;
else
r = (*fc.fs->getxattr)(&fc, &xattr);
if (r) {
DEBUG(("Fchown(%s): unable to get file attributes",name));
release_cookie(&fc);
return r;
}
if (xattr.uid != curproc->euid || xattr.uid != uid) {
DEBUG(("Fchown(%s): not the file's owner",name));
release_cookie(&fc);
return EACCDN;
}
}
r = (*fc.fs->chown)(&fc, uid, gid);
release_cookie(&fc);
return r;
}
/*
* GEMDOS extension: Fchmod(file, mode) changes a file's access
* permissions.
*/
long ARGS_ON_STACK
f_chmod(name, mode)
const char *name;
unsigned mode;
{
fcookie fc;
long r;
XATTR xattr;
TRACE(("Fchmod(%s, %o)", name, mode));
r = path2cookie(name, follow_links, &fc);
if (r) {
DEBUG(("Fchmod(%s): error %ld", name, r));
return r;
}
r = (*fc.fs->getxattr)(&fc, &xattr);
if (r) {
DEBUG(("Fchmod(%s): couldn't get file attributes",name));
}
else if (curproc->euid && curproc->euid != xattr.uid) {
DEBUG(("Fchmod(%s): not the file's owner",name));
r = EACCDN;
} else {
r = (*fc.fs->chmode)(&fc, mode & ~S_IFMT);
if (r) DEBUG(("Fchmod: error %ld", r));
}
release_cookie(&fc);
return r;
}
/*
* GEMDOS extension: Dlock(mode, dev): locks or unlocks access to
* a BIOS device. "mode" bit 0 is 0 for unlock, 1 for lock; "dev" is a
* BIOS device (0 for A:, 1 for B:, etc.).
*
* Returns: 0 if the operation was successful
* EACCDN if a lock attempt is made on a drive that is being
* used
* ELOCKED if the drive is locked by another process
* ENSLOCK if a program attempts to unlock a drive it
* hasn't locked.
* ++jr: if mode bit 1 is set, then instead of returning ELOCKED the
* pid of the process which has locked the drive is returned (unless
* it was locked by pid 0, in which case ELOCKED is still returned).
*/
PROC *dlockproc[NUM_DRIVES];
long ARGS_ON_STACK
d_lock(mode, dev)
int mode, dev;
{
PROC *p;
FILEPTR *f;
int i;
extern int aliasdrv[];
TRACE(("Dlock(%x,%c:)", mode, dev+'A'));
if (dev < 0 || dev >= NUM_DRIVES) return EDRIVE;
if (aliasdrv[dev]) {
dev = aliasdrv[dev] - 1;
if (dev < 0 || dev >= NUM_DRIVES)
return EDRIVE;
}
if ( (mode&1) == 0) { /* unlock */
if (dlockproc[dev] == curproc) {
dlockproc[dev] = 0;
changedrv(dev);
return 0;
}
DEBUG(("Dlock: no such lock"));
return ENSLOCK;
}
/* code for locking */
/* is the drive already locked? */
if (dlockproc[dev]) {
DEBUG(("Dlock: drive already locked"));
if (dlockproc[dev] == curproc) return 0;
if (dlockproc[dev]->pid == 0) return ELOCKED;
return (mode & 2) ? dlockproc[dev]->pid : ELOCKED;
}
/* see if the drive is in use */
for (p = proclist; p; p = p->gl_next) {
if (p->wait_q == ZOMBIE_Q || p->wait_q == TSR_Q)
continue;
for (i = MIN_HANDLE; i < MAX_OPEN; i++) {
if ( ((f = p->handle[i]) != 0) && (f->fc.dev == dev) ) {
DEBUG(("Dlock: process %d has an open handle on the drive", p->pid));
if (p->pid == 0) return EACCDN;
return (mode & 2) ? p->pid : EACCDN;
}
}
}
/* if we reach here, the drive is not in use */
/* we lock it by setting dlockproc and by setting all root and current
* directories referring to the device to a null file system
*/
for (p = proclist; p; p = p->gl_next) {
for (i = 0; i < NUM_DRIVES; i++) {
if (p->root[i].dev == dev) {
release_cookie(&p->root[i]);
p->root[i].fs = 0;
}
if (p->curdir[i].dev == dev) {
release_cookie(&p->curdir[i]);
p->curdir[i].fs = 0;
}
}
}
dlockproc[dev] = curproc;
return 0;
}